import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.util.Date;
import java.util.StringTokenizer;

import org.omg.CORBA.portable.OutputStream;


public final class WebServer {

    public static void main(String argv[]) throws Exception
    {
		// Establish the listen socket.
	    ServerSocket welcomeSocket = null;
	    
	    try {
			welcomeSocket = new ServerSocket(6789);
		} catch (BindException e) {
			System.out.println("Web server failed to start.  Port already in use.  Choose another port.");
			return;
		}
		catch (Exception e) {
			System.out.println("Web server failed to start. " + e.getLocalizedMessage() );
			e.printStackTrace();
			return;
		}
    	System.out.println("Web server started");
	    
    	
    	// Process HTTP service requests in an infinite loop.
    	while (true) {
    		// Listen for a TCP connection request.
    		Socket connectionSocket = welcomeSocket.accept(); 

    		// Construct an object to process the HTTP request message.
    		HttpRequest request = new HttpRequest( connectionSocket );
    		 
    		// Create a new thread to process the request.
    		Thread thread = new Thread(request);
    		 
    		// Start the thread.
    		thread.start();  		
    	}
    }
}

final class HttpRequest implements Runnable
{
    final static String CRLF = "\r\n";
    Socket socket;
    String fileName = null;

    // Constructor
    public HttpRequest(Socket socket) throws Exception 
    {
           this.socket = socket;
    }

    // Implement the run() method of the Runnable interface.
    public void run()
    {
    	BufferedReader is = null;
    	DataOutputStream os = null;
    	
 		try {

 	        is = 
 	            new BufferedReader(new
 	            InputStreamReader(socket.getInputStream()));
 	        
			processRequest(is);
			
			os = new DataOutputStream(socket.getOutputStream());
			
			processResponse(os);
			
		} catch (Exception e) {
			System.err.println("Error processing the web request:  " + e.getMessage());
			e.printStackTrace();
		}
		finally{
			try {
				is.close();
				os.close();
				this.socket.close();
			} catch (IOException e) {
				System.err.println("Unable to close socket connection");
			}
		}
    }

    
    private void processRequest(BufferedReader is) throws Exception
    {
    	System.out.println("Thread " + Thread.currentThread().getId());
    	System.out.println("*********** Begin Request **************");
        // Get a reference to the socket's input and output streams.
        //InputStream is = ?;
        //DataOutputStream os = ?;

        String requestLine = is.readLine();
         
        // Display the request line.
        System.out.println(requestLine);
         
        // Get and display the header lines.
        String headerLine = null;
        while ((headerLine = is.readLine()).length() != 0) {
                System.out.println(headerLine);
        }
        
    	System.out.println("************ End Request *************\n\n");

    	// Extract the filename from the request line.
    	StringTokenizer tokens = new StringTokenizer(requestLine);
    	tokens.nextToken();  // skip over the method, which should be "GET"
    	String fileName = tokens.nextToken();
    	 
    	// Prepend a "." so that file request is within the current directory.
    	this.fileName = "." + fileName;
    }
    
    
    private void processResponse(DataOutputStream os) throws Exception
    {

    	// Open the requested file.
    	FileInputStream fis = null;
    	boolean fileExists = true;
    	try {
    		URL resourceUrl = WebServer.class.getClassLoader().getResource(this.fileName);
    		if (resourceUrl == null)
    			fileExists = false;
    		else {
    			fis = new FileInputStream( resourceUrl.getPath().replaceAll("%20", " "));
    	        //BufferedReader br = new BufferedReader(r);
    		}    	      
    	} catch (FileNotFoundException e) {
    	        fileExists = false;
    	}

    	// Construct the response message.
    	String statusLine = null;
    	String contentTypeLine = null;
    	String entityBody = null;
        statusLine = "HTTP/1.1 200 OK" + CRLF;
    	if (fileExists) {
    	        contentTypeLine = "Content-type: " + 
    	               contentType( fileName ) + CRLF;
    	        contentTypeLine += CRLF;
    	} else {
    	        contentTypeLine = "Content-Type: " + "text/html" + CRLF;
    	        contentTypeLine += CRLF;
    	        entityBody = "<HTML>" + 
    	               "<HEAD><h2>Not Found</TITLE></h2>" +
    	               "<BODY>File " + fileName + " Not Found</BODY></HTML>" + CRLF;
    	}
    	
    	// Send the status line.
    	os.writeBytes(statusLine);
    	 
    	// Send the content type line.
    	os.writeBytes(contentTypeLine);
    	 
    	// Send a blank line to indicate the end of the header lines.
    	os.writeBytes(CRLF);

    	//PrintWriter out = new PrintWriter(socket.getOutputStream(), true);	
    	//out.println("Here is a response - " + new Date().toLocaleString() + CRLF);
    	
    	// Send the entity body.
    	if (fileExists) {
    	        sendBytes(fis, os);
    	        fis.close();
    	} else {
    	        os.writeBytes(entityBody);
    	}
    }

    
    private String contentType(String fileName) {
		if(fileName.endsWith(".htm") || fileName.endsWith(".html")) {
			return "text/html";
		}
		else if(fileName.endsWith(".jpeg") || fileName.endsWith(".jpg")) {
			return "image/jpeg";
		}
		else if(fileName.endsWith(".gif")) {
			return "image/gif";
		}
		else
			return "application/octet-stream";
    }
    
    private static void sendBytes(FileInputStream fis, java.io.OutputStream os) 
    throws Exception
    {
       // Construct a 1K buffer to hold bytes on their way to the socket.
       byte[] buffer = new byte[1024];
       int bytes = 0;
     
       // Copy requested file into the socket's output stream.
       while((bytes = fis.read(buffer)) != -1 ) {
          os.write(buffer, 0, bytes);
       }
    }

}
